package com.myrslok.jar.manifest;

import com.myrslok.GenericManifestHeader;
import com.myrslok.util.StringUtil;
import org.jetbrains.annotations.NotNull;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Represents a manifest of a JAR. Has no connection to a Manifest FILE, only contains
 * the logic to create the text.
 */
public class Manifest {

    static final String CREATED_BY = "Myrslok alpha";

    public java.util.jar.Manifest asJavaUtilManifest() {
        try {
            return new java.util.jar.Manifest(new ByteArrayInputStream(getText().getBytes()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static class Builder {
        @NotNull
        final private List<ManifestHeader> headers = new ArrayList<ManifestHeader>();

        public Builder() {
            addHeader(new GenericManifestHeader("Created-By", CREATED_BY));
            addHeader(new GenericManifestHeader("Manifest-Version", "1.0"));
        }

        public void addHeader(@NotNull final ManifestHeader header) {
            headers.add(header);
        }

        @NotNull
        public Manifest build() {
            Collections.sort(headers, ManifestHeaderComparator.INSTANCE);
            return new Manifest(headers);
        }

        private static class ManifestHeaderComparator implements Comparator<ManifestHeader> {
            public static ManifestHeaderComparator INSTANCE = new ManifestHeaderComparator();

            private ManifestHeaderComparator() {
            }

            @Override
            public int compare(@NotNull final ManifestHeader header1, @NotNull final ManifestHeader header2) {
                return getScore(header1.getLabel()) - getScore(header2.getLabel());
            }

            private int getScore(@NotNull final String label) {
                /*
                 * From http://download.oracle.com/javase/1.4.2/docs/guide/jar/jar.html:
                 * Manifest-Version and Signature-Version must be first, and in exactly that case
                 * (so that they can be recognized easily as magic strings). Other than that, the order of attributes within a main section is not significant.
                 */
                if ("Manifest-Version".equals(label)) {
                    return 1;
                } else if ("Signature-Version".equals(label)) {
                    return 2;
                } else {
                    return 3;
                }
            }
        }
    }

    @NotNull
    final private List<ManifestHeader> headers;

    public Manifest(@NotNull final List<ManifestHeader> headers) {
        this.headers = headers;
    }

    public String getText() {
        return StringUtil.join("\n", headers, new ManifestHeaderStringifier()) + "\n";
    }

    private static class ManifestHeaderStringifier implements StringUtil.Stringifier<ManifestHeader> {
        public static final ManifestHeaderStringifier INSTANCE = new ManifestHeaderStringifier();

        private ManifestHeaderStringifier() {
        }

        @Override
        public String toString(@NotNull ManifestHeader manifestHeader) {
            return manifestHeader.getLabel() + ": " + manifestHeader.getValue();
        }
    }
}
